/* Copyright (c) 2023 Renmin University of China
RMDB is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
        http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

#pragma once

#include <cassert>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
#include "defs.h"
#include "parser/ast.h"
#include "record/rm_defs.h"


struct TabCol {
    std::string tab_name;
    std::string col_name;
    std::string alias;
    ast::GroupByCond group_by_cond;

    friend bool operator<(const TabCol &x, const TabCol &y) {
        return std::make_pair(x.tab_name, x.col_name) < std::make_pair(y.tab_name, y.col_name);
    }
};

struct Value {
    ColType type;  // type of value
    union {
        int int_val;      // int value
        float float_val;  // float value
    };
    std::string str_val;  // string value

    std::shared_ptr<RmRecord> raw;  // raw record buffer

    std::vector<Value> values;

    void set_int(int int_val_) {
        type = TYPE_INT;
        int_val = int_val_;
    }

    void set_float(float float_val_) {
        type = TYPE_FLOAT;
        float_val = float_val_;
    }

    void set_str(std::string str_val_) {
        type = TYPE_STRING;
        str_val = std::move(str_val_);
    }

    void init_raw(int len) {
        raw = std::make_shared<RmRecord>(len);
        if (type == TYPE_INT) {
            assert(len == sizeof(int));
            *(int *) (raw->data) = int_val;
        } else if (type == TYPE_FLOAT) {
            assert(len == sizeof(float));
            *(float *) (raw->data) = float_val;
        } else if (type == TYPE_STRING) {
            if (len < (int) str_val.size()) {
                throw StringOverflowError();
            }
            memset(raw->data, 0, len);
            memcpy(raw->data, str_val.c_str(), str_val.size());
        }else{
            assert(false);
        }
    }

    bool equalTo(const Value& rhs) const {
        if(type == TYPE_INT && rhs.type == TYPE_FLOAT){
            return (std::abs(rhs.float_val - (float)int_val) < 1e-6);
        }else if(type == TYPE_FLOAT && rhs.type == TYPE_INT){
            return (std::abs(float_val - (float)rhs.int_val) < 1e-6);
        }
        if (type != rhs.type) {
            throw InternalError("cannot compare numeric type with string type");
        }
        return (type == TYPE_INT && int_val == rhs.int_val) ||
               (type == TYPE_FLOAT && float_val == rhs.float_val) ||
               (type == TYPE_STRING && str_val == rhs.str_val);
    }

    bool operator==(const Value& rhs) const {
        return equalTo(rhs);
    }

    bool operator!=(const Value& rhs) const {
        return !equalTo(rhs);
    }

    bool greaterThan(const Value& rhs) const {
        if(type == TYPE_INT && rhs.type == TYPE_FLOAT){
            return int_val > (int)rhs.float_val;
        }else if (type == TYPE_FLOAT && rhs.type == TYPE_INT) {
            return ((float_val - (float)rhs.int_val) > 1e-6);
        }
        if (type != rhs.type) {
            throw InternalError("cannot compare numeric type with string type");
        }
        return (type == TYPE_INT && int_val > rhs.int_val) ||
               (type == TYPE_FLOAT && float_val > rhs.float_val) ||
               (type == TYPE_STRING && str_val > rhs.str_val);
    }

    bool operator>(const Value& rhs) const {
        return greaterThan(rhs);
    }

    bool operator<(const Value& rhs) const {
        return !(equalTo(rhs) || greaterThan(rhs));
    }

    bool operator>=(const Value& rhs) const {
        return equalTo(rhs) || greaterThan(rhs);
    }
    bool operator<=(const Value& rhs) const {
        return !greaterThan(rhs);
    }
};

struct Select{
    std::shared_ptr<ast::SelectStmt> sel;
    Select(ast::SelectStmt sel_) : sel(std::make_shared<ast::SelectStmt>(std::move(sel_))){}
    Select(){};
};

enum CompOp {
    OP_EQ, OP_NE, OP_LT, OP_GT, OP_LE, OP_GE, FALSE, OP_IN
};

struct Condition {
    TabCol lhs_col;   // left-hand side column
    CompOp op;        // comparison operator
    bool is_rhs_val;  // true if right-hand side is a value (not a column)
    TabCol rhs_col;   // right-hand side column
    Value rhs_val;    // right-hand side value
    Select rhs_sel = Select();  // right-hand side select

    bool is_having = false;
    bool is_sel = false;

    bool eval_with_rvalue(const Value &lhs) const {
        assert(is_rhs_val);
        return eval(lhs, rhs_val);
    }

    bool eval(const Value &lhs, const Value &rhs) const{
        switch (op) {
            case OP_EQ:
                return lhs == rhs;
            case OP_NE:
                return lhs != rhs;
            case OP_LT:
                return lhs < rhs;
            case OP_GT:
                return lhs > rhs;
            case OP_LE:
                return lhs <= rhs;
            case OP_GE:
                return lhs >= rhs;
            case FALSE:
                return false;
            case OP_IN:{
                for(auto val : rhs_val.values){
                    if(lhs == val){
                        return true;
                    }
                }
                return false;
            }
            default:
                throw InternalError("op is not implemented");
        }
    }
};
struct SetClause {
    TabCol lhs;
    Value rhs;
    TabCol rhs_col;
    bool is_val;
};

class Query{
    public:
    std::shared_ptr<ast::TreeNode> parse;
    // TODO jointree
    // where条件
    std::vector<Condition> conds;
    // 投影列
    std::vector<TabCol> cols;
    // 表名
    std::vector<std::string> tables;
    // update 的set 值
    std::vector<SetClause> set_clauses;
    //insert 的values值
    std::vector<Value> values;
    // load的file path
    alignas(16) std::string file_path;

    bool has_group;
    std::vector<TabCol> group_cols;

    bool has_having;
    std::vector<Condition> having_conds;

    Query(){}

};